home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Workspace / aa_Intel_Only / TimeShift / timeshift.c next >
Encoding:
C/C++ Source or Header  |  1995-12-03  |  5.8 KB  |  232 lines

  1. /* timeshift
  2. ** This program is linked to 3 different names.
  3. **    timeshift
  4. **    halt
  5. **    reboot
  6. ** When executed via the names halt or reboot, the clock is set to
  7. ** what it would be if the local time zone was GMT.  After munging
  8. ** the clock, the real halt or reboot executable is then exec'ed.
  9. **
  10. ** When executed via the name timeshift, the clock is shifted back
  11. ** to the normal time within the local time zone.
  12. **
  13. ** In order to prevent shifting the clock an odd number of times
  14. ** and to prevent screwing up when shutdown and boot span a DST
  15. ** cusp, the amount of clock shift is stored in a lock file
  16. ** /usr/etc/shifted.time.
  17. **
  18. ** This software is provided as shareware.
  19. ** If you find this software useful, please send me $10
  20. **
  21. ** email feedback to jq@quick.com
  22. ** For current snail address refer to address returned from internic
  23. ** via 'whois quick.com'
  24. **
  25. ** $Header: /usr/users/jq/c/util/timeshift/RCS/timeshift.c,v 1.1 95/12/03 16:26:57 jq Exp $
  26. */
  27. #include <sys/param.h>
  28. #include <syslog.h>
  29. #include <libc.h>
  30. #include <sys/types.h>
  31. #include <sys/time.h>
  32. #include <sys/errno.h>
  33. #include <sys/file.h>
  34.  
  35. #define    TIME_LOCK    "/usr/etc/shifted.time"
  36.  
  37. extern int errno;
  38.  
  39. enum shift_mode {
  40.     UP = -1,
  41.     NOCHANGE = 0,
  42.     DOWN = 1
  43. };
  44.  
  45. /* Return the basename of a pathname
  46. */
  47. char *
  48. basename(char *path)
  49. {
  50.     char    *base = rindex(path, '/');
  51.  
  52.     if (base) {
  53.         base++;
  54.     } else {
  55.         base = path;
  56.     }
  57.  
  58.     return base;
  59. }
  60.  
  61. /* Shift the clock by the amount required to go from local time in time zone
  62. ** to local time in GMT when shutting down the computer.  Reverse the
  63. ** process on the way back up.
  64. ** N.B. The reason for saving the time shift to a file and using it on the
  65. ** way back up, is to prevent dropping or gaining an hour if the system
  66. ** is brought down under daylight savings time and then brought up in
  67. ** standard time, or vice versa.
  68. */
  69. void
  70. shift_time(enum shift_mode mode)
  71. {
  72.     struct timeval tp;
  73.     struct timezone tzone;
  74.     long    shift = 0;
  75.     int    err;
  76.     FILE    *stamp = 0;
  77.  
  78.     /* Let someone know why we did nothing.
  79.     */
  80.     if (mode == NOCHANGE) {
  81.         syslog(LOG_ALERT, "sync option prevented time change.");
  82.         return;
  83.     }
  84.  
  85.     /* Make sure that we do not set clock multiple timed in DOWN mode.
  86.     */
  87.     if (mode == DOWN && access(TIME_LOCK, F_OK) == 0) {
  88.         syslog(LOG_ALERT, "%s already exists. No change made.",
  89.             TIME_LOCK);
  90.         return;
  91.     }
  92.  
  93.     /* Open the TIME_LOCK file in read mode on the way up and in
  94.     ** write mode on the way down.
  95.     */
  96.     stamp = fopen(TIME_LOCK, (mode == UP)?"r":"w");
  97.     if (stamp == 0) {
  98.         syslog(LOG_ALERT, "Cannot fopen %s - %s",
  99.             TIME_LOCK,
  100.             strerror(errno));
  101.         return;
  102.     }
  103.  
  104.     /* On the way down, set shift amount and save it in TIME_LOCK.
  105.     ** On the way up, read the previous shift amount to undo it.
  106.     */
  107.     if (mode == DOWN) {
  108.         time_t    clock = 0;
  109.         struct tm *now;
  110.  
  111.         time(&clock);
  112.         now = localtime(&clock);
  113.         fprintf(stamp, "%ld\n", now->tm_gmtoff);
  114.         shift = mode * (now->tm_gmtoff);
  115.     } else if (mode == UP) {
  116.         fscanf(stamp, "%ld", &shift);
  117.         shift *= mode;
  118.     }
  119.     fclose(stamp);
  120.  
  121.     /* Now, get the current time, adjust for shift, and reset time.
  122.     ** Note that we are not accounting for the amount of time it 
  123.     ** will take to set the clock or performe the addition.
  124.     ** Though we could probably perform the operation and get a
  125.     ** rough guess of how many microseconds to fudge, I don't think
  126.     ** it's worth the effort.
  127.     */
  128.     gettimeofday(&tp, &tzone);
  129.     tp.tv_sec += shift;
  130.     err = settimeofday(&tp, &tzone);
  131.  
  132.     if (err == -1) {
  133.         /* If we failed to change the clock on the way down,
  134.         ** then unlink the TIME_LOCK so that we don't make
  135.         ** things worse on the way up.
  136.         */
  137.         if (mode == DOWN) {
  138.             unlink(TIME_LOCK);
  139.         }
  140.     } else {
  141.         /* If we did set the clock, get rid of our old stamp file
  142.         ** since we've undone the change it represents.
  143.         */
  144.         if (mode == UP) {
  145.             unlink(TIME_LOCK);
  146.         }
  147.         syslog(LOG_ALERT, "Clock adjusted by %d seconds\n", shift);
  148.     }
  149. }
  150.  
  151. /* Important safety checks!
  152. ** If 'reboot -n' or 'halt -n' are used it would be *extremely* uncool
  153. ** to do anything with the file system.  Since a sync is not performed
  154. ** you can end up allocating a block which will remain linked to a 
  155. ** directory entry, but actually be still on the free list.  Ouch!
  156. **
  157. ** I do not know if 'reboot -q' suffers the same problems but I'm
  158. ** erring on false positive here and bailing on any command which
  159. ** has either 'n' or 'q' as any part of a hyphenated argument.
  160. */
  161. enum shift_mode
  162. safety_check(int argc, char **argv)
  163. {
  164.     char    *arg;
  165.     int    i;
  166.  
  167.     for (i = 0; i < argc; i++) {
  168.         arg = argv[i];
  169.         if (*arg == '-' && (index(arg, 'n') || index(arg, 'q'))) {
  170.             return NOCHANGE; /* whew */
  171.         }
  172.     }
  173.  
  174.     return DOWN;
  175. }
  176.  
  177. int
  178. main(int argc, char **argv)
  179. {
  180.     char    *base = basename(argv[0]);
  181.     enum shift_mode mode = NOCHANGE;
  182.     char    realcmd[MAXPATHLEN + 1];
  183.     char    *exec_cmd = 0;
  184.     char    ishmael[128]; /* What to call me. */
  185.  
  186.  
  187.     /* First determine whether we are coming down or booting up.
  188.     */
  189.     if (strcmp(base,"timeshift") == 0) {
  190.         mode = UP;
  191.         strcpy(ishmael, base);
  192.     } else if (strcmp(base, "halt") == 0 || strcmp(base, "reboot") == 0) {
  193.         char    *prefix = "/usr/etc/";
  194.  
  195.         mode = safety_check(argc, argv);
  196.  
  197.         sprintf(ishmael, "%s (%s)", "timeshift", base);
  198.         if (index(argv[0], '/') != 0) {
  199.             prefix = "";
  200.         }
  201.         sprintf(realcmd, "%s%s.real", prefix, argv[0]);
  202.  
  203.         exec_cmd = realcmd;
  204.     } else {
  205.         syslog(LOG_ALERT, "%s: is not named reboot halt or shiftup\n", argv[0]);
  206.         exit(1);
  207.     }
  208.     openlog(ishmael, LOG_CONS, LOG_DAEMON);
  209.  
  210.     /* Next make sure we are root.
  211.     ** It's kind of gilding the lily a bit, but better safe than sorry.
  212.     */
  213.     if (geteuid() != 0) {
  214.         syslog(LOG_ALERT, "%s: not run as root.\n", argv[0]);
  215.         exit(1);
  216.     }
  217.  
  218.     /* Cover our asses in case we should just not do anything.
  219.     */
  220.     shift_time(mode);
  221.  
  222.     /* If we were called instead of the real halt or reboot
  223.     ** we now need to exec the real mcCoy.
  224.     */
  225.     if (exec_cmd) {
  226.         execvp(exec_cmd, argv);
  227.         syslog(LOG_ALERT, "Fatal error! Could't exec '%s'", realcmd);
  228.     }
  229.  
  230.     return 0;
  231. }
  232.